package com.suray.wcs.service.test.action.checkmap.core;

import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.path.enums.DirectionStatus;
import com.suray.basic.wcs.utils.Action;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.wcs.service.system.entity.NodeDB;
import com.suray.wcs.service.system.service.IWareHouseDBService;
import com.suray.wcs.service.system.service.impl.NodeDBServiceImpl;
import com.suray.wcs.service.test.action.checkmap.exception.PathNodeException;
import com.suray.wcs.service.test.action.checkmap.type.RgvDirection;
import com.suray.wcs.service.util.WcsUtil;

import java.util.*;

public class CheckMapGroundCode {
    public static Map<Integer, CheckMapGroundCode> checkMapNodeMap = new HashMap<>();

    private NodeDBServiceImpl nodeDBService = SpringContextHolder.getBean(NodeDBServiceImpl.class);
    private IWareHouseDBService wareHouseDBService = SpringContextHolder.getBean(IWareHouseDBService.class);

    /**
     * 节点
     */
    private List<Node> nodes;

    /**
     * 小车方向
     */
    private int direct;

    /**
     * 路径上的地码
     */
    private List<String> groundCodes;

    /**
     * 地码和点位的map
     */
    private Map<String, Node> groundCodeMap;

    /**
     * 上次小车换向方向
     */
    private DirectionStatus lastDirection;

    /**
     * 计算标志条码信息
     * @param nodes
     * @param directionStatus
     */
    public CheckMapGroundCode(List<Node> nodes, DirectionStatus directionStatus) {
        this.lastDirection = directionStatus;

        this.direct = wareHouseDBService.getCurWareHouseDB().getMapDirection();

        groundCodes = new ArrayList<>();
        groundCodeMap = new HashMap<>();

        this.nodes = supplyPath(nodes);
    }

    public List<String> getGroundCodes() {
        return new ArrayList<>(groundCodes);
    }

    /**
     * 补充路径节点
     * @param nodes
     * @return
     */
    private List<Node> supplyPath(List<Node> nodes) {
        Set<Node> nodeList = new HashSet<>();
        try {
            groundCodes.add(generalGroundCode(nodeDBService.getNodeDB(nodes.get(0)), "500"));

            for (int i = 1; i < nodes.size(); i++) {
                nodeList.addAll(calcLineNodes(nodes.get(i - 1), nodes.get(i)));
            }
        } catch (PathNodeException e) {
            throw new RuntimeException("节点路径错误，不在一条直线：" + WcsUtil.listToString(nodes));
        }

        return new ArrayList<>(nodeList);
    }

    /**
     * 计算两点间的节点
     * 两个点必须在一条直线上
     * @param startNode
     * @param endNode
     * @return
     */
    private List<Node> calcLineNodes(Node startNode, Node endNode) {
        if (startNode.equals(endNode)) {
            return new ArrayList<>();
        }

        return generaNodes(startNode, endNode);
    }

    /**
     * 根据起点终点生成两点间的坐标
     * @param startNode
     * @param endNode
     * @return
     */
    private List<Node> generaNodes(Node startNode, Node endNode) {
        int startX = startNode.getX(), startY = startNode.getY();
        int endX = endNode.getX(), endY = endNode.getY();
        int offsetX = endX == startX ? 0 : (endX - startX) / Math.abs(endX - startX);
        int offsetY = endY == startY ? 0 :(endY - startY) / Math.abs(endY - startY);
        int z = startNode.getZ();

        if (startNode.getZ() != endNode.getZ()) {
            throw new PathNodeException("起点与终点不在同一层，无法检测地码");
        }

        if (offsetX != 0 && offsetY != 0) {
            throw new PathNodeException("两点不在一条直线，无法补充路径节点");
        }

        List<Node> nodeList = new ArrayList<>();

        while (startX != endX || startY != endY) {
            nodeList.add(new Node(startX, startY, z));
            startX += offsetX;
            startY += offsetY;
        }

        nodeList.add(new Node(startX, startY, z));
        groundCodes.addAll(generalGroundCodes(analyzeNodeDBS(nodeList), offsetX, offsetY));
        return nodeList;
    }

    private void analyzeNodeGroundCode(Node node) {
        if (node.getResAction() == null) {
            return;
        }

        if (lastDirection == DirectionStatus.XD_13) {
            if (node.getResAction() == Action.CHANGE_TO_RAMP) {
                groundCodes.add(generalGroundCode(nodeDBService.getNodeDB(node), "500"));
            }
        } else if (lastDirection == DirectionStatus.PD_24) {
            if (node.getResAction() == Action.CHANGE_TO_ROADWAY) {
                groundCodes.add(generalGroundCode(nodeDBService.getNodeDB(node), "500"));
            }
        }
    }

    /**
     * 获取路径对应的配置
     * @param nodes
     * @return
     */
    private List<PathGroundCodeInfo> analyzeNodeDBS(List<Node> nodes) {
        List<NodeDB> allNodeDBs = nodeDBService.list();
        List<PathGroundCodeInfo> matchNodeDBs = new ArrayList<>();

        for (Node node : nodes) {
            for (NodeDB nodeDB : allNodeDBs) {
                if (node.equals(new Node(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz()))) {
                    matchNodeDBs.add(new PathGroundCodeInfo(nodeDB, node.getResAction()));
                    break;
                }
            }
        }

        return matchNodeDBs;
    }

    /**
     * 处理方向
     * @param direct
     * @return
     */
    private int calcDirect(int direct) {
        if (direct > 4) {
            direct -= 4;
        }

        return direct;
    }

    /**
     * 生成路径节点行驶过程中扫到的地码
     * @param pathGroundCodeInfos
     * @param xDirect
     * @param yDirect
     * @return
     */
    public List<String> generalGroundCodes(List<PathGroundCodeInfo> pathGroundCodeInfos, int xDirect, int yDirect) {
        RgvDirection direction;

        if (xDirect > 0) {
            //向下行驶
            direction = RgvDirection.parseDirection(direct);
        } else if (xDirect < 0) {
            //向上行驶
            direction = RgvDirection.parseDirection(calcDirect(direct + 2));
        } else if (yDirect > 0) {
            //向右行驶
            direction = RgvDirection.parseDirection(calcDirect(direct + 1));
        } else if (yDirect < 0) {
            //向左行驶
            direction = RgvDirection.parseDirection(calcDirect(direct + 3));
        } else {
            throw new RuntimeException("无法解析小车行驶方向：" + WcsUtil.listToString(pathGroundCodeInfos));
        }

        return analyzeDirectionCode(pathGroundCodeInfos, direction);
    }

    /**
     * 根据方向，生成地码队列
     * @param pathGroundCodeInfos
     * @param direction
     * @return
     */
    private List<String> analyzeDirectionCode(List<PathGroundCodeInfo> pathGroundCodeInfos, RgvDirection direction) {
        List<String> groundCodes = new ArrayList<>();
        String groundCode;

        /**
         * 需删除第一个节点的第一个码和最后一个节点的第三个码
         * 起点和终点只要两个码
         */
        switch (direction) {
            case ONE:
            case TWO:
                //增大
                //点位最后一个码
                groundCodes.add(generalGroundCode(pathGroundCodeInfos.get(0).nodeDB, "510"));

                for (int i = 1; i < pathGroundCodeInfos.size(); i++) {
                    groundCodes.addAll(generalNodeGroundCodes(pathGroundCodeInfos.get(i).nodeDB, true));
                }

                groundCode = groundCodes.remove(groundCodes.size() - 1);
                removeGroundCodeMap(groundCode);
                break;
            case THREE:
            case FOUR:
                //减小
                //点位最后一个码
                groundCodes.add(generalGroundCode(pathGroundCodeInfos.get(0).nodeDB, "490"));

                for (int i = 1; i < pathGroundCodeInfos.size(); i++) {
                    groundCodes.addAll(generalNodeGroundCodes(pathGroundCodeInfos.get(i).nodeDB, false));
                }

                groundCode = groundCodes.remove(groundCodes.size() - 1);
                removeGroundCodeMap(groundCode);
                break;
            default:
                throw new RuntimeException("小车方向错误：" + direct);
        }

        return groundCodes;
    }

    /**
     * 生成节点的所有地码
     * @param nodeDB:坐标点
     * @param increase:是否递增
     * @return
     */
    private List<String> generalNodeGroundCodes(NodeDB nodeDB, boolean increase) {
        List<String> groundCodes = new ArrayList<>();

        if (increase) {
            groundCodes.add(generalGroundCode(nodeDB, "490"));
            groundCodes.add(generalGroundCode(nodeDB, "500"));
            groundCodes.add(generalGroundCode(nodeDB, "510"));
        } else {
            groundCodes.add(generalGroundCode(nodeDB, "510"));
            groundCodes.add(generalGroundCode(nodeDB, "500"));
            groundCodes.add(generalGroundCode(nodeDB, "490"));
        }

        return groundCodes;
    }

    /**
     * 生成对应位置的地码
     * @param nodeDB
     * @param locationCode
     * @return
     */
    private String generalGroundCode(NodeDB nodeDB, String locationCode) {
        String ground = nodeDB.getGroundCode();
        String groundCode = ground + locationCode;
        addGroundCodeMap(groundCode, new Node(nodeDB.getGridx(), nodeDB.getGridy(), nodeDB.getGridz()));
        return groundCode;
    }

    private void addGroundCodeMap(String groundCode, Node node) {
        groundCodeMap.put(groundCode, node);
    }

    private void removeGroundCodeMap(String groundCode) {
        groundCodeMap.remove(groundCode);
    }

    public Node getNode(String groundCode) {
        return groundCodeMap.get(groundCode);
    }

    /**
     * 获取对应地码的map
     * @param groundCodes
     * @return
     */
    public Map<String, Node> getNodes(List<String> groundCodes) {
        Map<String, Node> matchCodeMap = new HashMap<>();

        groundCodes.forEach(groundCode -> {
            matchCodeMap.put(groundCode, groundCodeMap.get(groundCode));
        });

        return matchCodeMap;
    }

    @Override
    public String toString() {
        return "CheckMapGroundCode{" +
                ", nodes=" + WcsUtil.listToString(nodes) +
                ", direct=" + direct +
                ", groundCodes=" + WcsUtil.listToString(new ArrayList<>(groundCodes)) +
                '}';
    }
}
